package org.ikastola

import beans.FormReceiptRecord
import ikastola.Constants;

import java.text.SimpleDateFormat
import org.joda.time.Period;
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.transaction.annotation.Transactional;
import utils.Utils;
import grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class ReceiptController {

    static allowedMethods = [save: "POST", update: "POST", delete: "POST"]

	static dateUtilService
	
	static receiptService
	
    def index() {
        redirect(action: "list", params: params)
    }

    def list(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        [receiptInstanceList: Receipt.findAllByDeleted(Constants.NOT_DELETED, params), receiptInstanceTotal: Receipt.countByDeleted(Constants.NOT_DELETED)]
    }

    def create(Long id) {
		session.student = null
		session.formrecords = null
		session.discounts = null
		session.receipt = null
		session.balances = null
		session.checks = []
		
		if(!id) {
			flash.message = message(code: 'default.not.found.message', args: [message(code: 'student.label', default: 'Student'), id])
			redirect(controller: "student", action: "list")
			return
		}

		Student studentInstance = Student.get(id)
		if(!studentInstance || Constants.DELETED.equals(studentInstance.getDeleted())) {
			flash.message = message(code: 'default.not.found.message', args: [message(code: 'student.label', default: 'Student'), id])
			redirect(controller: "student", action: "list")
			return
		}
		
		Service serviceInstance = studentInstance.getService()
		
		if(!serviceInstance || !serviceInstance.fees) {
			flash.message = "El alumno "+studentInstance.name + " " + studentInstance.lastname + " no tiene Aranceles asignados para cobrar. Por favor editar agregando tarifas y descuentos" //message(code: 'default.not.found.message', args: [message(code: 'student.label', default: 'Student'), studentId])
			redirect(controller: "student", action: "list")
			return
		}
		
		//Receipt receiptInstance = new Receipt(params)
		
		//receiptInstance.setStudent(studentInstance)
		
		List<FormReceiptRecord> beans = new ArrayList<FormReceiptRecord>()
		Double total = 0.0
		Double total2 = 0.0 //total con descuentos y bonificaciones

		Set<Discount> discounts = []
		
		for (Fee fee in serviceInstance.getFees()) {
			
			FormReceiptRecord bean = new FormReceiptRecord()
			
			BalanceFees balance = receiptService.getFeeBalance(studentInstance, fee)
			
			Double interest = dateUtilService.getDueInterest(balance)

			bean.setFee(fee) 
			
			Double discValue = 0.0
			DateDiscount bonus = null
			bean.setPay(0)
			bean.setPeriod(balance.getPaymentdate())
			
			if (balance.getDue() == null) {
				
				discounts = studentInstance.getDisc()
				
				if (interest == 0.0) {
					if (!discounts) { //los alumnos que tienen descuentos no tienen bonificaciones por pago en termino
						bonus = dateUtilService.getDateDiscount(balance);
						if (bonus != null) {
							bean.setBonus(bonus.getValue())
							bean.setBonusDesc(bonus.getDescription())
						}
					}
				
					for (Discount d in discounts) { //los alumnos que no paguen en termino pierden el descuento 
						if (d.getFee().equals(fee)) {
							discValue = Utils.round(discValue + d.getValue())
						}
					}
				}
								
				bean.setValue(Utils.round(fee.getValue() - discValue))
				
				bean.setInterest(Utils.round(interest*bean.getValue()))
								
				bean.setDue(0)
				
				total = Utils.round(total + bean.getValue())
				
				bean.setFinalTotal(Utils.round(bean.getValue() + bean.getInterest() - bean.getBonus()))
				
				if (Constants.FEE_PERIOD_ANNUAL.equals(fee.getPeriod())) {
					bean.setExclude(1)
				}
			} else {
				
				bean.setInterest(Utils.round(interest*balance.getDue()))
			
				bean.setDue(balance.getDue())
				
				total = Utils.round(total + bean.getDue())
				
				bean.setFinalTotal(Utils.round(bean.getDue() + bean.getInterest()))
			}
			
			bean.setInterestPercent(interest)
			
			bean.setInterestDesc("Interes")
			
			total2 = Utils.round(total2 + bean.getFinalTotal())
			
			beans.add(bean)
		}
		//calcular todo lo necesario en esta action y guardar todo en sesion, no ir mas a buscar nada a la base
		//session.receipt = receiptInstance
		session.formrecords = beans
		session.discounts = discounts
		session.student = studentInstance
		//receiptInstance: receiptInstance
		[formRecords: beans, discounts: discounts, total: total, total2: total2, studentInstance: studentInstance]
    }
	
	/**
	 * 
	 * @return
	 */
	def confirm() {
				
		def receiptInstance
		def balanceFees
		def checkPayments

		try {
			Student student = session.student
			if(!student || Constants.DELETED.equals(student.getDeleted())) {
				throw new RuntimeException("No se han seleccionado Alumnos para el Comprobante")
			}
			
			if (params.paymentcheck) { //si esta seleccionado cheque
				if (!session.checks) {
					throw new RuntimeException("Se ha indicado 'Pago con cheque' pero no se han agregado cheques")
				}
				else { //se valida que la suma de montos de cheques no supere a lo indicado para pagar
					Double totalChecks = 0.0
					Double totalPayment = 0.0
					for (c in session.checks) {
						totalChecks = Utils.round(totalChecks + c.getAmount())
					} 
					for (k in params.keySet()) {
						if (k.startsWith("fee-")) {
							Double payFee = Double.parseDouble(params.getAt(k)) //lo que se indico a pagar por la tarifa
							totalPayment = Utils.round(totalPayment + payFee)
						}
					}
					if (totalChecks > totalPayment) {
						throw new RuntimeException("La suma de montos indicados en los cheques supera al total indicado para pagar. Cheques:"+ totalChecks + " A pagar:" + totalPayment)
					}
				}
			}
			
			Service serviceInstance = student.getService()
			if(!serviceInstance || !serviceInstance.fees) {
				throw new RuntimeException("El alumno "+student.name + " " + student.lastname
					+ " no tiene Aranceles asignados para cobrar. Por favor editar agregando tarifas y descuentos")
			}
			
			receiptInstance = receiptService.confirmReceiptInstance(params, student, session)
			
			balanceFees = receiptService.confirmBalanceFees(params, student, session)
		
		} catch(Exception e) {
			flash.message = e.message
			e.printStackTrace()
			redirect(action: "create", id: session.student.id)
			return
		}
		session.receipt = receiptInstance
		session.balances = balanceFees
		[receiptInstance: receiptInstance, balancesInstance : balanceFees, checks : session.checks]
	}
	
	/**
	 * 
	 * @return
	 */
	@Transactional
	def save() {
		//valida que Total sea mayor que 0 y deuda no sea negativa
		Receipt receiptInstance = session.receipt
		def balanceFees = session.balances
		if(receiptInstance.getTotal() < 0.0 || receiptInstance.getDue() < 0.0) {
			flash.message = "Error: Total y Deuda no pueden ser valores negativos"
			redirect(action: "list")
			return
		}

		try{
			if (session.checks) {
				for (c in session.checks) {
					c.setDeleted(Constants.NOT_DELETED)
					c.setClassCreated(new Date())
					receiptInstance.addToChecks(c)
				}
			}
			
			//guarda el recibo
//			for(ItemDetail d in receiptInstance.getDetails()) {
//				d.save(flush: true, failOnError: true)
//			}
			receiptInstance.setUuid('0000000');
			receiptInstance.setDeleted(Constants.NOT_DELETED)
			receiptInstance.setClassCreated(new Date())
			receiptInstance.save(flush: true, failOnError: true)
			
			//Actualizacion de fechas de pago en servicio del alumno:
			for (BalanceFees b in balanceFees) {
				b.setReceipt(receiptInstance)
				b.setDeleted(Constants.NOT_DELETED)
				b.save(flush: true, failOnError: true)
			}
			
			//receiptService.saveReceipt(session)
		}catch(Exception e){
			flash.message = e.message //message(code: 'default.created.message', args: [message(code: 'receipt.label', default: 'Receipt')],"")
			e.printStackTrace()
			redirect(action: "list")
			return
		}
		
		if (!receiptInstance.hasErrors()) {
			flash.message = message(code: 'default.created.message', args: [message(code: 'receipt.label', default: 'Receipt'), receiptInstance.id])
			redirect(action: "show", id: receiptInstance.id)
			return
		}
		else {
			flash.message = receiptInstance.getErrors()
			redirect(action: "list")
			return
		}
	}
	
    def show(Long id) {
        def receiptInstance = Receipt.get(id)
        if (!receiptInstance || Constants.DELETED.equals(receiptInstance.getDeleted())) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'receipt.label', default: 'Receipt'), id])
            redirect(action: "list")
            return
        }

        [receiptInstance: receiptInstance]
    }

    /*def edit(Long id) {
        def receiptInstance = Receipt.get(id)
        if (!receiptInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'receipt.label', default: 'Receipt'), id])
            redirect(action: "list")
            return
        }

        [receiptInstance: receiptInstance]
    }*/
	/*
    def update(Long id, Long version) {
        def receiptInstance = Receipt.get(id)
        if (!receiptInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'receipt.label', default: 'Receipt'), id])
            redirect(action: "list")
            return
        }

        if (version != null) {
            if (receiptInstance.version > version) {
                receiptInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
                          [message(code: 'receipt.label', default: 'Receipt')] as Object[],
                          "Another user has updated this Receipt while you were editing")
                render(view: "edit", model: [receiptInstance: receiptInstance])
                return
            }
        }

        receiptInstance.properties = params

        if (!receiptInstance.save(flush: true)) {
            render(view: "edit", model: [receiptInstance: receiptInstance])
            return
        }

        flash.message = message(code: 'default.updated.message', args: [message(code: 'receipt.label', default: 'Receipt'), receiptInstance.id])
        redirect(action: "show", id: receiptInstance.id)
    }*/
	
	
	/**
	 * 
	 * @param id
	 * @return
	 */
	@Transactional
    def delete(Long id) {
        def receiptInstance = Receipt.get(id)
        if (!receiptInstance || Constants.DELETED.equals(receiptInstance.getDeleted())) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'receipt.label', default: 'Receipt'), id])
            redirect(action: "list")
            return
        }

        try {
			def balances = BalanceFees.findAllByReceiptAndDeleted(receiptInstance, Constants.NOT_DELETED)
			for (BalanceFees b in balances) {
				b.setDeleted(Constants.DELETED)
				b.save(flush: true)
			}
			
			def checks = receiptInstance.getChecks()
			for (PaymentCheck c in checks) {
				c.setDeleted(Constants.DELETED)
				c.setClassUpdated(new Date())
				c.save(flush: true)
			}
			
			def items = receiptInstance.getDetails()
			for (ItemDetail i in items) {
				for (ItemDetailChild ic in i.getDetails()) {
					ic.setDeleted(Constants.DELETED)
					ic.setClassUpdated(new Date())
					ic.save(flush: true)
				}
				i.setDeleted(Constants.DELETED)
				i.setClassUpdated(new Date())
				i.save(flush: true)
			}
			 
			receiptInstance.setDeleted(Constants.DELETED)
			receiptInstance.setClassUpdated(new Date())
            receiptInstance.save(flush: true)
            
			flash.message = message(code: 'default.deleted.message', args: [message(code: 'receipt.label', default: 'Receipt'), id])
            redirect(action: "list")
        }
        catch (DataIntegrityViolationException e) {
            flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'receipt.label', default: 'Receipt'), id])
            redirect(action: "show", id: id)
        }
    }
	
	
	def addCheck(params) {
		def paymentCheckInstance = new PaymentCheck(params)
		paymentCheckInstance.setAmount(new Double(params.amount))
		def checks = []
		Long i = 0
		if(!session.checks) {
			session.checks = checks
		}
		checks = session.checks
		checks.add(paymentCheckInstance)
		session.checks = checks

		String names = ""
		for(PaymentCheck c in checks) {
			names = names + "<tr class='"+ ((i % 2) == 0 ? "even" : "odd") + "'><td>"+ c.getBank() + "</td><td>" + c.getNumber() + "</td><td>" + String.valueOf(c.getAmount()) + "</td><td>"  + remoteLink(update:'updateMe', action:'removeCheck',id: i) {"Eliminar"} + "</td></tr>"
			i++
		}
		
		String header = "<table><thead><tr><th>Banco</th><th>N&uacute;mero</th><th>Monto</th><th>Acci&oacute;n</th></tr></thead><tbody>"
		String tail = "</tbody></table>"
		
		String html = header + names + tail

		render(text: html, contentType: "text/html", encoding: "UTF-8")
	}
	
	def removeCheck(Long id) {
		def checks
		Long i = 0
		if(!session.checks) {
			return
		}
		checks = session.checks
		def check = checks.getAt(id.toInteger())
		checks = checks.minus(check)
		//System.out.println(checks)
		session.checks = checks
		String names = ""
		for(PaymentCheck c in checks) {
			names = names + "<tr class='"+ ((i % 2) == 0 ? 'even' : 'odd') + "'><td>"+ c.getBank() + "</td><td>" + c.getNumber() + "</td><td>" + String.valueOf(c.getAmount()) + "</td><td>" + remoteLink(update:'updateMe', action:'removeCheck',id: i) {"Eliminar"} + "</td></tr>"
			i++
		}
		
		String header = "<table><thead><tr><th>Banco</th><th>N&uacute;mero</th><th>Monto</th><th>Acci&oacute;n</th></tr></thead><tbody>"
		String tail = "</tbody></table>"
		
		String html = names.equals("") ? names : header + names + tail
		
		render(text: html, contentType: "text/html", encoding: "UTF-8")
	}
	
	def pdf(Long id) {
		def receiptInstance = Receipt.get(id)
		if (!receiptInstance) {
			flash.message = message(code: 'default.not.found.message', args: [message(code: 'receipt.label', default: 'Receipt'), id])
			redirect(action: "list")
			return
		}
		
		def invoiceModel = receiptService.getInvoiceModel(receiptInstance)
		
		SimpleDateFormat format = new SimpleDateFormat(Constants.DATE_FORMAT_DEFAULT)
		String cdate = format.format(receiptInstance.getCreationdate())
		String sfullName = receiptInstance.getStudent().getFullName()
		Course course = receiptInstance.getStudent().getCourse()
		String scourse = course.getName() + ' ' + course.getDivision() + ' ' + course.getShiftDescription() 
		params.customerName = sfullName
		params.customerCourse = scourse
		params.invoiceNumber = receiptInstance.getId()
		params.customerDoc = receiptInstance.getStudent().getDoctypeDesc() +" "+receiptInstance.getStudent().getDocnumber() 
		params.invoiceAmount = String.valueOf(receiptInstance.getTotal())
		params.invoiceDate = cdate
		params.poNumber =  receiptInstance.getStudent().getId()
		params._format = "PDF"
		params.logo = g.resource(dir: 'images', file: 'logoik.png', absolute: true)
		params._name = String.valueOf(receiptInstance.getId())+"_"+sfullName.replaceAll(" ","")+"_"+cdate
		params._file = "invoice"
		params.totaldue = String.valueOf(receiptInstance.getDue())
		
		chain(controller:'jasper',action:'index',model:[data:invoiceModel],params:params)
	}
	
	def search() {
		def receiptInstanceList
		def studentInstanceList = Student.findAllByNameIlike('%'+params.query+'%')
		if(!studentInstanceList) {
			studentInstanceList = Student.findAllByLastnameIlike('%'+params.query+'%')
		}
		if(!studentInstanceList) {
			receiptInstanceList = []
		} else {
			receiptInstanceList = Receipt.findAllByStudentInList(studentInstanceList)
		}
		render(view: 'list', model: [receiptInstanceList: receiptInstanceList, receiptInstanceTotal: receiptInstanceList.size])
	}
}
